package com.rfid.jni;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.rfid.RFIDCallException;
import com.rfid.RFIDCaller;

/**
 * 该类利用JNI调用CPP，CPP调用驱动程序对RFID硬件进行操作，
 * 底层将在正常完成时返回相应的数据，遇到错误将抛出如下：<br>
 * @see RFIDCallException 该异常通常是无法正确执行程序引起的<br>
 * @see RFIDStatusException 该异常是由于设备以驱动错误引起的，异常中带有相应的状态码，详情请参照该异常类
 * @author zzm
 * 
 */
public class JNI_RFIDCaller implements RFIDCaller {
	
	static {
		System.loadLibrary("JNI_RFIDFunctions");
	}
	
	private static final Logger log = Logger.getLogger(JNI_RFIDCaller.class);
	
	public static final int AREA_SIZE = 48;
	
	private Properties config = new Properties();
	
	private int minAreaNum;
	private int maxAreaNum;
	private int onceReadMinAreaCount;
	private int onceReadMaxAreaCount;
	
	public JNI_RFIDCaller() throws IOException {
		InputStream is = this.getClass().getResourceAsStream("config.properties");
		try {
			config.load(is);
		} finally {
			if(is != null) is.close();
		}
		try {
			this.maxAreaNum = Integer.parseInt(config.getProperty("MaxAreaNum"));
			this.minAreaNum = Integer.parseInt(config.getProperty("MinAreaNum"));
			this.onceReadMaxAreaCount = Integer.parseInt(config.getProperty("OnceReadMaxAreaCount"));
			this.onceReadMinAreaCount = Integer.parseInt(config.getProperty("OnceReadMinAreaCount"));
		} catch(NumberFormatException e) {
			throw new RuntimeException("config.properties file has some error, error string: " + e.getMessage());
		}
	}

	@Override
	public void ring() throws RFIDCallException {
		try {
			_ring();
			if(log.isDebugEnabled()) {
				log.debug("ring");
			}
		} catch(Throwable t) {
			doThrow(t);
		}
	}

	@Override
	public String read(int areaCount, int areaStarts) throws RFIDCallException {
		checkArea(areaCount, areaStarts);
		if(log.isDebugEnabled()) {
			log.debug("reading..[areaCount:" + areaCount + ", areaStarts:" + areaStarts + "]");
		}
		try {
			return _read(areaCount, areaStarts);
		} catch(Throwable t) {
			doThrow(t);
		}
		return null;
	}

	@Override
	public void write(int areaCount, int areaStarts, String data)
			throws RFIDCallException {
		checkArea(areaCount, areaStarts);
		int dataLength = data.getBytes().length;
		if(dataLength > areaCount * AREA_SIZE) {
			throw new IllegalArgumentException("Data too long: " + dataLength + ">" + areaCount * AREA_SIZE);
		}
		if(log.isDebugEnabled()) {
			log.debug("writing..[areaCount:" + areaCount + ", areaStarts:" + areaStarts + ", data:" + data + "]");
		}
		try {
			_write(areaCount, areaStarts, data);
		} catch(Throwable t) {
			doThrow(t);
		}
	}
	
	private void doThrow(Throwable t) throws RFIDCallException {
		if(t instanceof RFIDCallException) {
			throw (RFIDCallException) t;
		}
		throw new RFIDCallException(t);
	}
	
	private void checkArea(int areaCount, int areaStarts) {
		if(areaCount < this.onceReadMinAreaCount || areaCount > this.onceReadMaxAreaCount) {
			throw new IllegalArgumentException("areaCount must between " + this.onceReadMinAreaCount + " and " + this.onceReadMaxAreaCount);
		}
		int start = areaStarts;
		int end = areaStarts + areaCount - 1;
		if(start < this.minAreaNum || end > this.maxAreaNum) {
			throw new IllegalArgumentException("Illegal argument areaStarts: " + areaStarts);
		}
	}
	
	private native void  _ring();
	
	private native String _read(int areaCount, int areaStarts);
	
	private native void _write(int areaCount, int aresStarts, String date);

}
